Skip to content

release: merge current main-target PR set#318

Merged
ndycode merged 502 commits intomainfrom
release/mainbound-20260323
Mar 23, 2026
Merged

release: merge current main-target PR set#318
ndycode merged 502 commits intomainfrom
release/mainbound-20260323

Conversation

@ndycode
Copy link
Owner

@ndycode ndycode commented Mar 22, 2026

Summary

Merge the current main-target PR set through a single verified release branch.

Included PRs: #140 #141 #149 #263 #298 #299 #300 #302 #303 #304 #306 #307 #308 #309

Additional release fixes

  • fix optional dashboard display dependency in forecast command tests
  • tighten backend settings schema max for sessionAffinityMaxEntries
  • treat EACCES as retryable for queued settings writes
  • fix docs parity coverage for extracted switch command docs
  • correct backup metadata latest-valid-path selection priority
  • add storage regressions around named-backup export snapshot isolation

Local validation

  • npm run clean:repo:check
  • npm run typecheck
  • npm run build
  • npm run lint
  • npm test -- test/storage.test.ts test/named-backup-export.test.ts test/storage-recovery-paths.test.ts test/backup-metadata-builder.test.ts
  • npx vitest run --maxWorkers=1 --silent --shard=1/4
  • npx vitest run --maxWorkers=1 --silent --shard=2/4
  • npx vitest run --maxWorkers=1 --silent --shard=1/8
  • npx vitest run --maxWorkers=1 --silent --shard=2/8
  • npx vitest run --maxWorkers=1 --silent --shard=3/8
  • npx vitest run --maxWorkers=1 --silent --shard=4/8
  • npx vitest run --maxWorkers=1 --silent --shard=5/8

Known local blocker

  • larger remaining Vitest shard runs hit a local worker OOM before any concrete failing assertion was identified; this PR exists to get the authoritative Ubuntu/Node CI result on the combined release state before merging to main.

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

large release branch merging 14 prs. core areas touched: session-affinity gets lastResponseId threading for response continuation; named-backup-export fixes a real concurrency race (in-flight guard now set before first await); backup-metadata-builder introduces correct priority-ordered snapshot selection; import-export fixes the previously flagged negative importedCount bug; settings-write-queue adds EACCES to retryable codes (helpful on windows, mild false-positive on linux); the codex-manager surface is heavily refactored into focused sub-modules.

  • prior review concern (proactiveRefreshGuardian/proactiveRefreshIntervalMs unreachable in UI) is resolved — both are now in the refresh-recovery category
  • prior review concern (importedCount going negative) is resolvedmergeImportedAccounts now deduplicates existing accounts before computing the delta
  • rememberLastResponseId is a zero-value wrapper around updateLastResponseId; the jsdoc references a non-existent rememberWithResponseId upsert method — clean up or implement the referenced method
  • sessionAffinityMaxEntries zod schema lacks .max(4_096) to match the ui cap, so direct config edits can grow the in-memory map unboundedly
  • EACCES in RETRYABLE_SETTINGS_WRITE_CODES is correct for windows file locks but adds ~800ms of wasted retry time on linux/macOS for true permission-denied errors

Confidence Score: 4/5

  • safe to merge after addressing the missing .max() on sessionAffinityMaxEntries and the dangling rememberWithResponseId jsdoc reference
  • both previously flagged correctness issues are resolved; the three remaining comments are p2 style/hardening items — none block the primary user path or introduce data loss risk. the concurrency race in named-backup-export is properly fixed. the oom-blocked vitest shards are a local resource constraint, not a code defect.
  • lib/schemas.ts (missing max bound), lib/session-affinity.ts (redundant public method + dangling jsdoc), lib/codex-manager/settings-write-queue.ts (EACCES on non-windows)

Important Files Changed

Filename Overview
lib/session-affinity.ts adds lastResponseId to affinity entries with getLastResponseId, updateLastResponseId, and a redundant rememberLastResponseId wrapper; eviction logic correctly extracted to setEntry; dangling jsdoc reference to non-existent rememberWithResponseId
lib/codex-manager/settings-write-queue.ts new per-path write queue with exponential-backoff retry; EACCES added as retryable which is correct for windows file locks but will cause silent 800ms delay on linux/macOS permanent permission errors
lib/named-backup-export.ts concurrency fix: inFlightNamedBackupExports.add(exportKey) moved before the first await, closing the race window where two concurrent exports to the same path could both pass the in-flight guard; force no longer bypasses the concurrent-export lock (correct)
lib/storage/backup-metadata-builder.ts new builder correctly implements priority-first snapshot selection: discovered-backup (4) > backup-history (3) > backup (2) > wal (1) > primary (0), with cascading fallback for wal and then primary; latestValidPath override replaces buildMetadataSection's candidate when a higher-priority backup exists
lib/storage/import-export.ts addresses prior review concern: importedCount now compares deduplicatedAccounts vs deduplicatedExistingAccounts (not raw existingAccounts), preventing negative counts when existing storage has internal duplicates; atomic write via temp-file + rename with windows retry
lib/schemas.ts adds responseContinuation and backgroundResponses boolean fields; sessionAffinityMaxEntries UI cap (4_096) is set in backend-settings-schema but the Zod schema here still has no .max(), allowing arbitrarily large values from direct config edits
lib/codex-manager/backend-settings-schema.ts new file; both proactiveRefreshGuardian (toggle) and proactiveRefreshIntervalMs (number) are now present in the refresh-recovery category, resolving the prior review concern about those controls being unreachable in the UI
lib/storage/named-backups.ts new collector for named backup summaries; correctly detects mtime drift between stat-before and stat-after load (windows antivirus window), logs a debug warning rather than failing; filters empty/invalid snapshots before returning

Sequence Diagram

sequenceDiagram
    participant Caller
    participant SessionAffinityStore
    participant EntriesMap

    Note over Caller,EntriesMap: response continuation flow (new)
    Caller->>SessionAffinityStore: remember(sessionKey, accountIndex, now)
    SessionAffinityStore->>EntriesMap: get(key) → existingEntry (preserve lastResponseId)
    SessionAffinityStore->>SessionAffinityStore: setEntry(key, {accountIndex, lastResponseId: existing})
    SessionAffinityStore->>EntriesMap: evict oldest if at maxEntries, then set

    Caller->>SessionAffinityStore: updateLastResponseId(sessionKey, responseId, now)
    SessionAffinityStore->>EntriesMap: get(key) → entry
    alt entry missing or expired
        SessionAffinityStore-->>Caller: no-op (delete if expired)
    else entry live
        SessionAffinityStore->>SessionAffinityStore: setEntry(key, {...entry, lastResponseId, expiresAt refreshed})
        SessionAffinityStore-->>Caller: void
    end

    Caller->>SessionAffinityStore: getLastResponseId(sessionKey, now)
    SessionAffinityStore->>EntriesMap: get(key)
    alt expired
        SessionAffinityStore->>EntriesMap: delete(key)
        SessionAffinityStore-->>Caller: null
    else live
        SessionAffinityStore-->>Caller: lastResponseId (trimmed) or null
    end
Loading

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/session-affinity.ts
Line: 95-107

Comment:
**`rememberLastResponseId` is a dead-alias wrapper with a dangling JSDoc reference**

`rememberLastResponseId` is a one-line forwarding shell for `updateLastResponseId` and adds no behaviour on top of it. the jsdoc says callers that need to upsert continuation state should use `rememberWithResponseId`, but that method does not exist anywhere in the codebase — so the comment actively misleads future engineers looking for an upsert path.

either drop the wrapper and have callers use `updateLastResponseId` directly, or implement the missing `rememberWithResponseId` upsert method and point the jsdoc at it correctly. leaving two identical public methods increases the surface for concurrency confusion if one ever diverges.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: lib/schemas.ts
Line: 49

Comment:
**`sessionAffinityMaxEntries` Zod schema missing `.max()` bound**

the pr description says "tighten backend settings schema max for `sessionAffinityMaxEntries`", and the UI control in `backend-settings-schema.ts` correctly caps the value at `4_096`. however the Zod schema here has no corresponding `.max()` — a user editing the config JSON directly can set an arbitrarily large value (e.g. `1_000_000`) and it will pass validation, growing the in-memory `Map` unboundedly. the `SessionAffinityStore` constructor only floors via `Math.max(1, …)`, so no runtime guard catches it.

```suggestion
	sessionAffinityMaxEntries: z.number().min(8).max(4_096).optional(),
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: lib/codex-manager/settings-write-queue.ts
Line: 8

Comment:
**`EACCES` is not transient on linux/macOS — consider scoping to windows**

`EACCES` (permission denied) is included as a retryable code. on windows this can be a transient file-lock, which is the intent. on linux/macOS `EACCES` is a hard permission denial that will not resolve on retry, so all 4 attempts will fail and callers receive the error ~800ms later than expected. consider noting this in a comment, or gating on `process.platform === "win32"` for `EACCES` specifically to avoid silent slowdowns on posix hosts when account-storage permissions are actually wrong.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (3): Last reviewed commit: "Fix release branch review follow-ups" | Re-trigger Greptile

ndycode added 30 commits March 22, 2026 07:09
This was referenced Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants